@file:OptIn(ExperimentalFoundationApi::class)

package com.example.result.books

import android.content.Context
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.RocketLaunch
import androidx.compose.material.icons.filled.Save
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.AssistChip
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ElevatedAssistChip
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.InputChip
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SuggestionChip
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.core.text.isDigitsOnly
import com.example.result.ui.theme.LocalMessage
import com.lly.tboc.zhouyi.Screen
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.flow.withIndex
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.yield
import java.nio.charset.Charset

data class Section(val id: Int, val info: String)

@Composable
fun TextShowAndEdit(
    fileAddress: Screen.WebView.FileAddress,
    fileOperation: FileOperation,
    modifier: Modifier = Modifier
) {
    val context = LocalContext.current
    val source = remember(fileAddress) {
        mutableStateOf("")
    }
    val currentEncode by context.textEncodeRecodeDataStore.data
        .map {
            it.data.firstOrNull { it.key == fileAddress.path }?.value
                ?: encodes[0]
        }
        .collectAsState(
            initial = encodes[0]
        )

    extractedSource(
        fileAddress = fileAddress,
        source = source,
        context = context,
        encode = Charset.forName(currentEncode)
    )
    val renameDialogState = mutableStateOf(false)
    RenameDialog(renameDialogState, fileAddress, fileOperation, source)
    var needSyncSourceChanged by remember {
        mutableStateOf(false)
    }
    val fileOperationAdapter = object : FileOperationAdapter() {
        override fun saveToFile() {
            needSyncSourceChanged = !needSyncSourceChanged

            renameDialogState.value = true
        }

    }
    val selectedPattern: String by getSectionPattern(context, fileAddress)
    val scope = rememberCoroutineScope()
    ShowOrEdit(modifier, currentEncode, updateEncode(scope, context, fileAddress),
        fileOperationAdapter,
        sourceView = {
            DefaultSourceEdit(
                source = source,
                needSyncSourceChanged,
                outScope = scope,
                onValueChange = { source.value = it })
        }) {
        var texts by remember {
            mutableStateOf(ResultWrap(data = emptyList<List<String>>()))
        }
        var sections by remember {
            mutableStateOf(ResultWrap(emptyList<String>()))
        }

        if (source.value.isNotEmpty()) {
            LaunchedEffect(source.value, selectedPattern) {
                val value = source.value
                texts = ResultWrap(emptyList())
                sections = ResultWrap(emptyList())
                val textData = mutableListOf<List<String>>()
                val sectionData = mutableListOf<String>()
                suspend fun Sequence<String>.collectData() {
                    val temp = mutableListOf<List<String>>()
                    val action = {
                        textData.addAll(temp)
                        texts = ResultWrap(textData)
                        sectionData.addAll(temp.map {
                            it.first { it.isNotBlank() }
                                .subStringByLineBreakAndLengthMostAt(100)
                        })
                        sections = ResultWrap(sectionData)
                        temp.clear()
                    }
                    asFlow().filter { it.isNotBlank() }
                        .map { it.split('\n') }.buffer(10).flowOn(Dispatchers.IO)
                        .collect {
                            temp.add(it)
                            if (temp.size > 10) {
                                action()
                                yield()
                            }
                        }
                    action()
                }
                withContext(Dispatchers.IO) { value.splitToSequence(selectedPattern.toRegex()) }.collectData()
                if (textData.size <= 1) {
                    textData.clear()
                    sectionData.clear()
                    withContext(Dispatchers.IO) { value.chunkedSequence(CHUNCK) }.collectData()
                }
                texts = ResultWrap(textData, true)
                sections = ResultWrap(sectionData, true)
            }
        }

        val pagerState = rememberPagerState(0, 0f) { texts.data.size }
        var searchJumpInfo by remember {
            mutableStateOf(SearchResult("", 0, 0))
        }
        val searchKeyword = remember {
            mutableStateOf("")
        }

        LazyRow {
            item {
                val showDialog = remember {
                    mutableStateOf(false)
                }
                SearchDialog(showDialog = showDialog, jumpAction = {
                    scope.launch {
                        pagerState.scrollToPage(it.chapter)
                        searchJumpInfo = it
                    }
                }, data = texts.data, searchKeyword)
                IconButton(
                    onClick = {
                        showDialog.value = true
                    },
                ) {
                    Icon(Icons.Default.Search, contentDescription = "search")
                }
            }

            val sectionData = sections.data
            if (sectionData.isNotEmpty()) {
                item {
                    val selectionSectionState = rememberLazyListState()
                    val selectSection = remember {
                        mutableStateOf(false)
                    }
                    ElevatedAssistChip(modifier = Modifier.padding(end = 4.dp),
                        onClick = {
                            scope.launch {
                                selectSection.value = true
                                selectionSectionState.animateScrollToItem(pagerState.currentPage)
                            }
                        },
                        label = {
                            Text(
                                text = sectionData.getOrNull(pagerState.currentPage) ?: "",
                                modifier = Modifier
                                    .widthIn(max = 120.dp)
                                    .basicMarquee(), maxLines = 1
                            )
                        })
                    SectionSelectDialog(
                        selectSection,
                        selectionSectionState,
                        sections,
                        pagerState,
                        scope
                    )
                }
            }
            item {
                SectionPatternSelectOrConfig(selectedPattern) { selectedPattern ->
                    scope.launch {
                        context.textSectionPatternRecordDataStore.updateData {
                            val list =
                                it.data.filterNot { it.key == fileAddress.path }.take(19)
                                    .toMutableList()
                            list.add(0, RecordInfo(fileAddress.path, selectedPattern))
                            TextSectionPatternRecord(list)
                        }
                    }
                }
            }

        }

        HorizontalPager(
            modifier = Modifier
                .fillMaxWidth()
                .fillMaxHeight(),
            state = pagerState
        ) { pId ->
            Surface(
                color = MaterialTheme.colorScheme.secondaryContainer,
                modifier = Modifier.fillMaxHeight()
            ) {
                texts.data.getOrNull(pId)?.let { lines ->
                    val listState = rememberLazyListState()
                    val jumpNo by derivedStateOf {
                        if (searchJumpInfo.fragment.isNotEmpty() && searchJumpInfo.chapter == pId) searchJumpInfo.section
                        else -1
                    }
                    if (jumpNo != -1) {
                        LaunchedEffect(key1 = jumpNo) {
                            listState.animateScrollToItem(jumpNo)
                            searchJumpInfo = SearchResult()
                        }

                    }
                    val background = MaterialTheme.colorScheme.tertiary
                    val color = MaterialTheme.colorScheme.onTertiary
                    LazyColumn(
                        state = listState,
                        modifier = Modifier
                            .padding(vertical = 16.dp)
                            .simpleVerticalScrollbar(listState)
                    ) {
                        if (searchKeyword.value.isNotEmpty()) {

                            items(lines.map {
                                buildAnnotatedString {
                                    it.split(searchKeyword.value).forEachIndexed { index, s ->
                                        if (index != 0) {
                                            withStyle(
                                                style = SpanStyle(
                                                    color = color,
                                                    background = background,
                                                    fontWeight = FontWeight.Bold
                                                )
                                            ) {
                                                append(searchKeyword.value)
                                            }
                                        }
                                        append(s)
                                    }
                                }
                            }) { text ->
                                Text(
                                    text = text,
                                    modifier = Modifier
                                        .fillMaxWidth()
                                        .padding(horizontal = 16.dp)
                                )

                            }
                        } else {
                            items(lines) { text ->
                                Text(
                                    text = text,
                                    modifier = Modifier
                                        .fillMaxWidth()
                                        .padding(horizontal = 16.dp)
                                )
                            }
                        }
                    }

                }
            }
        }

    }
}

@Composable
private fun SectionSelectDialog(
    selectSection: MutableState<Boolean>,
    selectionSectionState: LazyListState,
    sections: ResultWrap<List<String>>,
    pagerState: PagerState,
    scope: CoroutineScope
) {
    var filterInfo by remember {
        mutableStateOf("")
    }
    var filteredSections: List<Section> by remember {
        mutableStateOf(emptyList())
    }
    LaunchedEffect(key1 = sections, key2 = filterInfo) {
        filteredSections = sections.data.toList().asFlow().withIndex().map {
            yield()
            Section(it.index, it.value)
        }.filter { it.info.contains(filterInfo) }
            .toList()
    }
    if (selectSection.value) {
        Dialog(onDismissRequest = {
            selectSection.value = false
        }) {
            var jumpLineNo by remember {
                mutableStateOf(0)
            }
            val keyboardController = LocalSoftwareKeyboardController.current
            Card {
                TextField(
                    modifier = Modifier.fillMaxWidth(),
                    value = filterInfo,
                    prefix = { Text(text = "过滤:") },
                    trailingIcon = {
                        Button(onClick = {
                            keyboardController?.hide()
                            filterInfo = ""
                        }) {
                            Text(text = "清理")
                        }
                    },
                    onValueChange = {
                        filterInfo = it
                    })
                LazyColumn(
                    state = selectionSectionState,
                    modifier = Modifier
                        .heightIn(100.dp, 500.dp)
                        .padding(4.dp)
                        .simpleVerticalScrollbar(selectionSectionState)
                        .fillMaxWidth()
                        .background(MaterialTheme.colorScheme.primaryContainer)
                ) {
                    itemsIndexed(filteredSections) { i, section ->
                        InputChip(
                            selected = section.id == pagerState.currentPage,
                            onClick = {
                                selectSection.value = false
                                scope.launch {
                                    pagerState.scrollToPage(section.id)
                                }
                            },
                            label = {
                                Text(text = "$i. ${section.info}")
                            })
                    }
                }
                TextField(
                    modifier = Modifier.fillMaxWidth(),
                    value = "$jumpLineNo",
                    label = {
                        Text(text = "共:${filteredSections.size}")
                    },
                    prefix = { Text(text = "第") },
                    suffix = { Text(text = "章") },
                    trailingIcon = {
                        Button(onClick = {
                            keyboardController?.hide()
                            scope.launch {
                                selectionSectionState.animateScrollToItem(jumpLineNo)
                            }
                        }) {
                            Text(text = "跳转")
                        }
                    },
                    keyboardOptions = KeyboardOptions.Default.copy(
                        keyboardType = KeyboardType.Number,
                        imeAction = ImeAction.Go
                    ),
                    keyboardActions = KeyboardActions {
                        keyboardController?.hide()
                        scope.launch {
                            selectionSectionState.animateScrollToItem(
                                jumpLineNo
                            )
                        }
                    },
                    onValueChange = {
                        if (it.isNotEmpty() && it.isDigitsOnly()) {
                            jumpLineNo = it.toInt()
                        }
                    })

            }
        }
    }
}

@Composable
fun Modifier.simpleVerticalScrollbar(
    state: LazyListState,
    width: Dp = 4.dp,
    color: Color = MaterialTheme.colorScheme.tertiary
): Modifier {
    val targetAlpha = if (state.isScrollInProgress) 1f else 0f
    val duration = if (state.isScrollInProgress) 150 else 500
    val alpha by animateFloatAsState(
        targetValue = targetAlpha,
        animationSpec = tween(durationMillis = duration), label = ""
    )

    return drawWithContent {
        drawContent()

        val firstVisibleElementIndex = state.layoutInfo.visibleItemsInfo.firstOrNull()?.index
        val needDrawScrollbar = state.isScrollInProgress || alpha > 0.0f

        // Draw scrollbar if scrolling or if the animation is still running and lazy column has content
        if (needDrawScrollbar && firstVisibleElementIndex != null) {
            val elementHeight = this.size.height / state.layoutInfo.totalItemsCount
            val scrollbarOffsetY = firstVisibleElementIndex * elementHeight
            val scrollbarHeight = state.layoutInfo.visibleItemsInfo.size * elementHeight

            drawRect(
                color = color,
                topLeft = Offset(this.size.width - width.toPx(), scrollbarOffsetY),
                size = Size(width.toPx(), scrollbarHeight),
                alpha = alpha
            )
        }
    }
}

@Composable
fun getSectionPattern(
    context: Context,
    fileAddress: Screen.WebView.FileAddress
) = context.textSectionPatternRecordDataStore.data.map {
    it.data.firstOrNull { it.key == fileAddress.path }?.value
        ?: context.sectionPatternsDataStore.data.first().patterns[0]
}.collectAsState(initial = "")

@Composable
fun getEncode(
    context: Context,
    fileAddress: Screen.WebView.FileAddress
) = context.textEncodeRecodeDataStore.data.map {
    it.data.firstOrNull { it.key == fileAddress.path }?.value ?: encodes[0]
}.collectAsState(initial = encodes[0])

fun updateEncode(
    scope: CoroutineScope,
    context: Context,
    fileAddress: Screen.WebView.FileAddress
): (String) -> Unit = { currentEncode ->
    scope.launch {
        context.textEncodeRecodeDataStore.updateData {
            val list = it.data.filterNot { it.key == fileAddress.path }.take(19).toMutableList()
            list.add(0, RecordInfo(fileAddress.path, currentEncode))
            TextEncodeRecord(list)
        }
    }
}

class SearchResultWrap(
    val keyword: String = "",
    val isCompleted: Boolean = false,
    val list: List<SearchResult> = emptyList()
) {
    override fun equals(other: Any?): Boolean {
        return this === other
    }

    override fun hashCode(): Int {
        var result = keyword.hashCode()
        result = 31 * result + isCompleted.hashCode()
        result = 31 * result + list.hashCode()
        return result
    }
}

data class SearchResult(val fragment: String = "", val chapter: Int = -1, val section: Int = -1)
class ResultWrap<T>(val data: T, val isCompleted: Boolean = false) {
    override fun equals(other: Any?): Boolean {
        return this === other
    }

    override fun hashCode(): Int {
        var result = data?.hashCode() ?: 0
        result = 31 * result + isCompleted.hashCode()
        return result
    }
}

@Composable
fun SearchDialog(
    showDialog: MutableState<Boolean>,
    jumpAction: (SearchResult) -> Unit,
    data: List<List<String>>,
    searchKeyword: MutableState<String>
) {
    var searchResult by remember {
        mutableStateOf(SearchResultWrap())
    }
    var selectedItem by remember {
        mutableStateOf(SearchResult())
    }
    LaunchedEffect(data) {
        selectedItem = SearchResult()
        searchResult = SearchResultWrap()
    }

    val listState = rememberLazyListState()
    if (showDialog.value) {
        val current = LocalMessage.current
        var searchState by remember {
            mutableStateOf(false)
        }
        val contextCount = 20
        val keyword = searchKeyword.value
        if (keyword.isNotEmpty()) {
            if (searchResult.keyword != keyword || !searchResult.isCompleted) {
                LaunchedEffect(key1 = keyword) {
                    searchResult = withContext(Dispatchers.IO) {
                        searchState = true
                        val reg =
                            ".{0,${contextCount}}$keyword.{0,${contextCount}}".toRegex()
                        val tem = mutableListOf<SearchResult>()
                        data.forEachIndexed { chapterId, strings ->
                            strings.forEachIndexed { sectionId, s ->
                                for (matchResult in reg.findAll(s)) {
                                    tem.add(
                                        SearchResult(
                                            matchResult.value,
                                            chapterId,
                                            sectionId
                                        )
                                    )
                                }
                                ensureActive()
                            }
                            searchResult = SearchResultWrap(keyword, false, tem)
                        }
                        SearchResultWrap(keyword, true, tem)
                    }
                    searchState = false
                    current.msg.value =
                        "$keyword 搜索完成:${searchResult.list.size}条"
                }
            } else {
                current.msg.value =
                    "$keyword 搜索缓存:${searchResult.list.size}条"
            }
        } else {
            searchResult = SearchResultWrap()
        }

        Dialog(onDismissRequest = { showDialog.value = false }) {
            val keyboardController = LocalSoftwareKeyboardController.current

            Card {
                var input by remember {
                    mutableStateOf(searchKeyword.value)
                }
                TextField(
                    modifier = Modifier.fillMaxWidth(),
                    value = input,
                    prefix = { Text(text = "搜索词:") },
                    trailingIcon = {
                        Row {
                            IconButton(onClick = {
                                keyboardController?.hide()
                                searchKeyword.value = input
                            }) {
                                Icon(Icons.Default.RocketLaunch, contentDescription = "search")
                            }
                            IconButton(onClick = {
                                keyboardController?.hide()
                                input = ""
                            }) {
                                Icon(Icons.Default.Clear, contentDescription = "clear")
                            }
                        }

                    },
                    keyboardOptions = KeyboardOptions.Default.copy(
                        imeAction = ImeAction.Go
                    ),
                    keyboardActions = KeyboardActions {
                        keyboardController?.hide()
                        searchKeyword.value = input
                    },
                    onValueChange = {
                        input = it
                    })
                val items = searchResult.list
                val count = items.size
                val searchProgressDescription =
                    if (searchState) "已搜到$count,搜索进行中" else "搜索完成，共${count}条"
                SuggestionChip(onClick = { }, label = {
                    Text(text = searchProgressDescription)
                }, modifier = Modifier.padding(4.dp))
                LazyColumn(
                    state = listState,
                    modifier = Modifier
                        .heightIn(100.dp, 500.dp)
                        .padding(4.dp)
                        .fillMaxWidth()
                        .simpleVerticalScrollbar(listState)
                        .background(MaterialTheme.colorScheme.primaryContainer)
                ) {
                    items(searchResult.list, key = { it.toString() }) {
                        InputChip(selected = it == selectedItem,
                            onClick = {
                                selectedItem = it
                                jumpAction(it)
                                showDialog.value = false
                            },
                            label = {
                                Text(text = it.fragment)
                            })
                    }
                }

            }

        }
    }
}

@Composable
fun SectionPatternSelectOrConfig(
    currentSelectedPattern: String,
    whenSelectedPattern: (String) -> Unit
) {
    var isShowDialog by remember {
        mutableStateOf(false)
    }
    val context = LocalContext.current
    val scope = rememberCoroutineScope()
    val sectionPatternsDataStore = context.sectionPatternsDataStore
    val patterns =
        sectionPatternsDataStore.data.collectAsState(initial = SectionPatterns()).value.patterns
    AssistChip(onClick = { isShowDialog = true }, label = {
        Text(
            text = "章节规则切换或配置", modifier = Modifier
                .basicMarquee()
                .widthIn(max = 200.dp), maxLines = 1
        )
    })
    DropdownMenu(expanded = isShowDialog, onDismissRequest = { isShowDialog = false }) {
        patterns.forEachIndexed { index, it ->
            val p = it.replace("\n", "\\n")
            DropdownMenuItem(text = {
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .horizontalScroll(state = rememberScrollState()),
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    var editing by remember {
                        mutableStateOf(false)
                    }
                    if (editing) {
                        var text by remember {
                            mutableStateOf(p)
                        }
                        TextField(value = text, onValueChange = {
                            text = it
                        }, trailingIcon = {
                            IconButton(onClick = {
                                scope.launch {
                                    sectionPatternsDataStore.updateData {
                                        val list = it.patterns.toMutableList()
                                        list[index] = text.replace("\\n", "\n")
                                        SectionPatterns(list)
                                    }
                                }
                                editing = false
                            }) {
                                Icon(Icons.Default.Save, contentDescription = "save")
                            }
                        })
                    } else {
                        InputChip(selected = currentSelectedPattern == patterns[index], onClick = {
                            whenSelectedPattern(patterns[index])
                            isShowDialog = false
                        }, label = { Text(text = p) })

                    }
                    IconButton(onClick = {
                        scope.launch {
                            sectionPatternsDataStore.updateData {
                                it.patterns.filterIndexed { i, _ -> i != index }
                                    .let { SectionPatterns(it) }
                            }
                        }
                    }) { Icon(Icons.Default.Delete, contentDescription = "delete") }
                    IconButton(onClick = { editing = true }) {
                        Icon(Icons.Default.Edit, contentDescription = "edit")
                    }
                    IconButton(onClick = {
                        scope.launch {
                            sectionPatternsDataStore.updateData {
                                val list = it.patterns.toMutableList()
                                list.add(p)
                                SectionPatterns(list)
                            }
                        }
                    }) {
                        Icon(Icons.Default.Add, contentDescription = "add")
                    }
                }
            }, onClick = {
            })
        }
    }
}
